class Snake {
  // 蛇头
  head: HTMLElement;
  // 蛇身
  bodies: HTMLCollection;
  // 容器
  element: HTMLElement;
  constructor() {
    this.element = document.getElementById('snake')!;
    this.head = document.querySelector('#snake > div') as HTMLElement;
    this.bodies = this.element.getElementsByTagName('div')!;
  }

  // 获取蛇头x坐标
  get x() {
    return this.head.offsetLeft;
  }

  // 获取蛇头y坐标
  get y() {
    return this.head.offsetTop;
  }

  set x(val: number) {
    if (this.x === val) {
      return;
    }

    // 判断是否掉头,当蛇头和蛇头后一节身体重叠时,即发生掉头
    if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === val) {
      // 当修改值大于当前值,说明想控制向右走,则只需让蛇继续向左走即可
      if (val > this.x) {
        val = this.x - 10;
      } else {
        val = this.x + 10;
      }
    }

    // 进入判断说明蛇撞墙了
    if (val < 0 || val > 290) {
      throw new Error('GAME OVER!');
    }

    // 移动身体
    this.moveBody();
    // 移动头
    this.head.style.left = `${val}px`;
    // 检查头和身体是否碰撞
    this.checkCollision();
  }

  set y(val: number) {
    if (this.y === val) {
      return;
    }

    // 判断是否掉头,当蛇头和蛇头后一节身体重叠时,即发生掉头
    if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === val) {
      // 当修改值大于当前值,说明想控制向下走,则只需让蛇继续向上走即可
      if (val > this.y) {
        val = this.y - 10;
      } else {
        val = this.y + 10;
      }
    }

    // 进入判断说明蛇撞墙了
    if (val < 0 || val > 290) {
      throw new Error('GAME OVER!');
    }
    this.moveBody();
    this.head.style.top = `${val}px`;
    this.checkCollision();
  }

  // 增加蛇身体的长度
  addBody() {
    this.element.insertAdjacentHTML('beforeend', '<div></div>');
  }

  // 将后一节身体的位置设置为前一节身体的位置
  // 第四节的位置改成第三节,第三节改成第二节,第二节改成蛇头的位置
  moveBody() {
    for (let i = this.bodies.length - 1; i > 0; i--) {
      let x = (this.bodies[i - 1] as HTMLElement).offsetLeft;
      let y = (this.bodies[i - 1] as HTMLElement).offsetTop;

      (this.bodies[i] as HTMLElement).style.left = `${x}px`;
      (this.bodies[i] as HTMLElement).style.top = `${y}px`;
    }
  }

  // 检查头和身体是否相撞,如果蛇头的最新位置和身体的任意节点重合,则发生相撞
  checkCollision() {
    for (let i = 1; i < this.bodies.length; i++) {
      let body = this.bodies[i] as HTMLElement;
      if (this.x === body.offsetLeft && this.y === body.offsetTop) {
        throw new Error('GAME OVER!');
      }
    }
  }
}

export default Snake;
